#! /usr/bin/env python3
"""
LDAP 代理（仅解析特定格式的LDAPSearchRequest）
- 只处理包含 uid equalityMatch 过滤器的搜索请求
- 格式：LDAPSearchRequest(..., filter=LDAPFilter_equalityMatch(attributeDesc=BEROctetString(value=b'uid'), assertionValue=BEROctetString(value=b'zhangsan')), ...)
"""

from ldaptor.protocols import pureldap
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapconnector import connectToLDAPEndpoint
from ldaptor.protocols.ldap.proxybase import ProxyBase
from twisted.internet import defer, protocol, reactor, threads
from twisted.python import log
from functools import partial
import sys
import requests
import os
from datetime import datetime, timedelta

FACE_API_URL = os.getenv("FACE_API_URL", "face-api-url")

g_start = datetime.now() - timedelta(minutes=3)
def is_in_2min(start_time: datetime) -> bool:
    current = datetime.now()
    end = start_time + timedelta(minutes=2)
    return start_time <= current <= end

class FaceRecognitionProxy(ProxyBase):
    def handleProxiedResponse(self, response, request, controls):
        log.msg(f"Request => {repr(request)}")
        log.msg(f"Response => {repr(response)}")

        # 仅处理特定格式的LDAPSearchRequest
        if isinstance(request, pureldap.LDAPSearchRequest) and isinstance(response, pureldap.LDAPSearchResultDone):
            # 提取并验证过滤器
            target_uid = self._extract_uid_from_filter(request.filter)
            if target_uid:
                log.msg(f"[FaceRecog] ✅ 找到符合条件的uid: {target_uid}")
                # 异步执行人脸识别
                d = threads.deferToThread(self._call_face_recog_api, target_uid)
                d.addCallback(self._handle_face_recog_result, request, response, target_uid)
                return d
            else:
                log.msg(f"[FaceRecog] ℹ️ 非目标格式的搜索请求，直接转发")
                return defer.succeed(response)
        else:
            return defer.succeed(response)

    def _extract_uid_from_filter(self, filter_obj):
        """
        仅提取特定格式的过滤器中的uid值：
        LDAPFilter_equalityMatch(attributeDesc=BEROctetString(value=b'uid'), assertionValue=BEROctetString(value=b'zhangsan'))
        """
        try:
            # 验证过滤器类型是否为equalityMatch
            if isinstance(filter_obj, pureldap.LDAPFilter_equalityMatch):
                # 验证属性是否为uid
                if (hasattr(filter_obj.attributeDesc, 'value') and 
                    filter_obj.attributeDesc.value == b'uid'):
                    # 提取uid值
                    if hasattr(filter_obj.assertionValue, 'value'):
                        uid_value = filter_obj.assertionValue.value.decode('utf-8')
                        return uid_value
            return None
        except Exception as e:
            log.msg(f"[FaceRecog] ❌ 解析过滤器失败: {str(e)}")
            return None

    def _call_face_recog_api(self, target_uid):
        """调用人脸识别API验证目标uid"""
        face_api_url = FACE_API_URL
        #"http://192.168.1.6:5000/verify"
        log.msg(f"face api url: {face_api_url}")
        try:
            log.msg(f"[FaceRecog] 📞 调用API验证 uid={target_uid}")
            response = requests.post(
                face_api_url,
                json={"username": target_uid},
                headers={"Content-Type": "application/json"},
                timeout=120
            )
            response.raise_for_status()
            return response.json().get("success", False)
        except Exception as e:
            log.msg(f"[FaceRecog] ❌ API调用错误: {str(e)}")
            return False

    def _handle_face_recog_result(self, recog_success, original_request, original_response, target_uid):
        """处理人脸识别结果"""
        if recog_success:
            log.msg(f"[FaceRecog] ✅ uid={target_uid} 人脸识别通过，返回搜索结果")
            return original_response
        else:
            log.msg(f"[FaceRecog] ❌ uid={target_uid} 人脸识别失败，拒绝访问")
            return pureldap.LDAPSearchResultDone(
                resultCode=50,
                errorMessage=b"Permission denied (face recognition failed)"
            )


# 绑定请求日志脱敏
def ldapBindRequestRepr(self):
    l = []
    l.append(f'version={self.version}')
    l.append(f'dn={repr(self.dn)}')
    l.append('auth=****')
    if self.tag != self.__class__.tag:
        l.append(f'tag={self.tag}')
    l.append(f'sasl={repr(self.sasl)}')
    return f"{self.__class__.__name__}({', '.join(l)})"

pureldap.LDAPBindRequest.__repr__ = ldapBindRequestRepr


if __name__ == '__main__':
    # 配置参数
    proxiedEndpointStr = 'tcp:host=openldap:port=389'
    use_tls = False
    listen_port = 3890

    # 初始化日志
    log.startLogging(sys.stderr)
    log.msg("=" * 70)
    log.msg(f"[Proxy] 人脸识别 LDAP 代理已启动")
    log.msg(f"[Proxy] 监听：0.0.0.0:{listen_port}")
    log.msg(f"[Proxy] 后端 LDAP：{proxiedEndpointStr}")
    log.msg(f"[Proxy] 仅处理包含 uid equalityMatch 过滤器的搜索请求")
    log.msg("=" * 70)

    # 创建后端连接器
    clientConnector = partial(
        connectToLDAPEndpoint,
        reactor,
        proxiedEndpointStr,
        LDAPClient
    )

    # 构建协议
    def buildProtocol():
        proto = FaceRecognitionProxy()
        proto.clientConnector = clientConnector
        proto.use_tls = use_tls
        return proto

    # 启动代理
    factory = protocol.ServerFactory()
    factory.protocol = buildProtocol
    reactor.listenTCP(listen_port, factory)
    reactor.run()
